// -*-mode:c++; coding:utf-8-*-

#if 0

#include "global.hpp"
#include "tablet_server_configuration.hpp"

#include "block_cache_unit.hpp"

namespace xbase
{
	bool block_cache_unit::commit(cache_node *node) {
		boost::mutex::scoped_lock guard(m_lock);
		if(get_node(node->m_key) != 0) // already has
		{
			LOG(ERROR, "block_cache_unit::commit_and_checkout node already exists, @"
			    << node << ":" << node->m_key.m_file_id << "#" << node->m_key.m_block_index
			    << ";" << node->m_value.m_block_size << "," << node->m_value.ref_count());
			debug_dump_cache_list("commit", node);
			return false;
		}
		std::pair<node_map_type::iterator, bool>
			ret = m_node_map.insert(node_map_type::value_type(node->m_key, node));
		if(ret.second) // insert ok
		{
			update_time(); // 要在检查淘汰前更新时间
			check_eliminate(); // 先进行淘汰检查，否则新加入的块可能会被淘汰到

			node->m_value.reset_ref_count(0); // clear
			push_back(node);
			
			m_block_data_size += node->m_value.m_block_size;
			m_block_number ++;
		}

		debug_dump_cache_list("commit", node);
		return ret.second;
	}

	bool block_cache_unit::commit_and_checkout(cache_node *node) {
		boost::mutex::scoped_lock guard(m_lock);
		if(get_node(node->m_key) != 0) // already has
		{
			LOG(ERROR, "block_cache_unit::commit_and_checkout node already exists, @"
			    << node << ":" << node->m_key.m_file_id << "#" << node->m_key.m_block_index
			    << ";" << node->m_value.m_block_size << "," << node->m_value.ref_count());
			debug_dump_cache_list("commit_and_checkout", node);
			return false;
		}
		std::pair<node_map_type::iterator, bool>
			ret = m_node_map.insert(node_map_type::value_type(node->m_key, node));
		if(ret.second) // insert ok
		{
			update_time(); // 要在检查淘汰前更新时间
			check_eliminate(); // 也是先检查淘汰吧，新加入的块肯定是不会被淘汰的

			node->m_value.reset_ref_count(1); // set to 1
			push_front(node);

			m_block_data_size += node->m_value.m_block_size;
			m_block_number ++;
		}

		debug_dump_cache_list("commit_and_checkout", node);
		return ret.second;
	}

	cache_node *block_cache_unit::checkout(const cache_key &key) {
		boost::mutex::scoped_lock guard(m_lock);
		cache_node *node = get_node(key);
		if(node != 0) // has
		{
			node->m_value.inc_ref_count();
			move_to_front(node);
			update_time();
		}

		debug_dump_cache_list("checkout", node);
		return node;
	}

	bool block_cache_unit::checkout(cache_node *node) {
		boost::mutex::scoped_lock guard(m_lock);
		const cache_node * const inner_node = get_node(node->m_key);
		if(inner_node != node) // not commited(not in my list)
		{
			LOG(ERROR, "block_cache_unit::checkout(cache_node*) node not exists, @"
			    << node << ":" << node->m_key.m_file_id << "#" << node->m_key.m_block_index
			    << ";" << node->m_value.m_block_size << "," << node->m_value.ref_count());
			debug_dump_cache_list("checkout(cache_node*)", node);
			return false;
		}
		
		node->m_value.inc_ref_count();
		move_to_front(node);
		update_time();
		
		debug_dump_cache_list("checkout(cache_node*)", node);
		return true;
	}
	
	bool block_cache_unit::checkin(cache_node *node) {
		boost::mutex::scoped_lock guard(m_lock);
		const cache_node * const inner_node = get_node(node->m_key);
		if(inner_node != node) // not commited
		{
			LOG(ERROR, "block_cache_unit::checkin node not matched: given @"
			    << node << ":" << node->m_key.m_file_id << "#" << node->m_key.m_block_index
			    << ";" << node->m_value.m_block_size << "," << node->m_value.ref_count()
			    << "; but has @" << inner_node
			    << ":" << inner_node->m_key.m_file_id << "#" << inner_node->m_key.m_block_index
			    << ";" << inner_node->m_value.m_block_size << "," << inner_node->m_value.ref_count());
			debug_dump_cache_list("checkin", node);
			return false;
		}
		update_time(); // 要在检查淘汰前更新时间
		if(node->m_value.dec_ref_count_and_test())
		{
			move_to_back(node);
			debug_dump_cache_list("checkin", node); // 必须在淘汰检查前进行，否则node可能会被释放掉
			check_eliminate();
		}
		else
		{
			debug_dump_cache_list("checkin", node);
		}
		return true;
	}

	bool block_cache_unit::is_timeout() {
		return (cur_seconds() - m_last_used_time)
			> static_cast<time_t>(global::the_conf->block_cache_lifetime);
	}

	void block_cache_unit::check_eliminate() {
		uint32_t max_cache_size = 16 * 1024 * 1024; // default to be 16M
		{
			table_schema::group_info::pointer group = get_group_info();
			if(! group)
			{
				LOG(ERROR, "block_cache_unit::check_eliminate get null group info for group "
				    << m_key.m_group_name
				    << ", ignore it and use default max cache size "
				    << max_cache_size);
			}
			else
			{
				max_cache_size = group->m_max_cache_size;
			}
		}

		// 
		// 淘汰规则：
		//
		// 按用户设置的最大缓存大小M，生成一个缓存区间，区间的大小是用户设置大小的1/L，当前取L=4，即
		// [M_a, M_b] => [M - M/(L * 2), M + M/(L * 2)] ==(M=64MB)=> [56MB, 72MB](16MB)
		// 当且仅当实际缓存大小M’＞M_b，才进行淘汰；如果开始淘汰，则一直淘汰到满足M'＜M_a为止
		//
		// 这样做，也以避免频繁的释放内存，而是集中一次进行释放（虽然也是多次调用）。
		// 

		const uint32_t shift_bits = 3; // 除以（2×4 = 8）,即右移3位
		const uint32_t /**/range_half_width = max_cache_size >> shift_bits;
		const uint32_t /*M_a*/lower_bound = max_cache_size - range_half_width;
		const uint32_t /*M_b*/upper_bound = max_cache_size + range_half_width;

		if(m_block_data_size > upper_bound) // > upper_bound, begin eliminate
		{
			LOG_ENV(const int count_block = m_block_number);
			LOG_ENV(const int count_bytes = m_block_data_size);

			clear_cache_until_busy_block_or_data_bytes_not_great_than( lower_bound );

			LOG(DEBUG, "block_cache_unit::check_eliminate eliminated "
			    << count_block - m_block_number << " unused block cache(s) with "
			    << count_bytes - m_block_data_size << " bytes, and left "
			    << m_block_number << " block cache(s) with "
			    << m_block_data_size << " bytes by cache_size_range ["
			    << lower_bound << "," << upper_bound
			    << "] in group " << m_key.m_group_name);
		}
	}

	void block_cache_unit::check_eliminate_for_timeout() {
		// 尝试获取锁，若无法获取，则肯定是正在使用中，不需要检查
		boost::mutex::scoped_try_lock try_guard(m_lock, boost::try_to_lock);
		if(try_guard.owns_lock())
		{
			// 如果超时，则淘汰所有未被使用的块，而
			// 不管缓存大小是否超过最大大小。这一机
			// 制使得很久没被使用的缓存得以淘汰。
			if((cached_block_number() > 0) // 只在有缓存的时候才检查是否超时
			   && is_timeout())
			{
				LOG(INFO, "block_cache_unit::check_eliminate cache unit {"
				    << m_key.m_table_name << ","
				    << m_key.m_tablet_name << ","
				    << m_key.m_group_name
				    << "} is not used too long, "
				    << "eliminate all unused caches");

				LOG_ENV(const int count_block = m_block_number);
				LOG_ENV(const int count_bytes = m_block_data_size);

				// TODO: 是否考虑内存使用率，仅当可用内存不太大时，才启用此功能？
				clear_cache_until_busy_block_or_data_bytes_not_great_than( 0 );

				LOG(INFO, "block_cache_unit::check_eliminate eliminate "
				    << count_block - m_block_number << " unused block cache(s) with "
				    << count_bytes - m_block_data_size << " bytes, and left "
				    << m_block_number << " block cache(s) with "
				    << m_block_data_size << " bytes");
				// 如果在长时间未使用的情况下淘
				// 汰，仍没有淘汰所有块，则打印
				// 警告日志。因为可能有些地方长
				// 时间的保持块而未使用，或发生
				// 了死锁！
				LOG_IF((m_block_number != 0),
				       WARN,
				       "block_cache_unit::check_eliminate "
				       << m_block_number
				       << " block(s) are STILL in use after VERY LONG IDLE, "
				       << "please CHECK this for keeping-block error or dead lock");
			}
		}
	}
	
	table_schema::pointer block_cache_unit::get_schema() const {
		if (m_schema.expired())
		{
			boost::mutex::scoped_lock _guard(m_schema_lock);
			if (m_schema.expired())
			{
				m_schema = global::the_schema_manager->get_schema(m_key.m_table_name);
			}
		}
		return m_schema.lock();
	}

	table_schema::group_info::pointer block_cache_unit::get_group_info() const {
		if (m_group_info.expired())
		{
			table_schema::pointer _schema = this->get_schema();
			if (! _schema)
			{
				LOG(ERROR, "sstable::get_group_info table schema of '"
				    << m_key.m_table_name << "' is null.");
			}
			else
			{
				boost::mutex::scoped_lock _guard(m_group_info_lock);
				if (m_group_info.expired())
				{
					m_group_info = _schema->get_group_info(m_key.m_group_name);
				}
			}
		}
		return m_group_info.lock();
	}

#ifndef NDEBUG_BLOCK_CACHE
	void block_cache_unit::debug_dump_cache_list(const char *name, cache_node *node)
	{
		std::ostringstream out;
		out << "cache unit:" << m_key.m_table_name
		    << "," << dump_string_char(m_key.m_tablet_name)
		    << "," << m_key.m_group_name
		    << "; block_data_size: " << m_block_data_size
		    << "; block_number: " << m_block_number
		    << std::endl;
		std::size_t i=0;
		bool node_flag = false;
		for(cache_node_list_type::iterator iter = m_list.begin();
		    iter != m_list.end(); ++iter, ++i)
		{
			char flag[3];
			flag[0] = flag[1] = ' ';
			flag[2] = '\0';
			if(node == &(*iter))
			{
				flag[0] = '*';
				node_flag = true;
			}
			if(m_water_line == &(*iter))
			{
				flag[1] = '~';
			}
			
			out << "\t" << flag << "[" << i
			    << "] cache_key: " << iter->m_key.m_file_id
			    << "#" << iter->m_key.m_block_index
			    << "; cache_value: " << iter->m_value.m_block_size
			    << "," << iter->m_value.ref_count()
			    << std::endl;
		}
		if((node_flag == false) && (node != NULL))
		{
			out << "\t* [-] " << node->m_key.m_file_id
			    << "#" << node->m_key.m_block_index
			    << "; cache_value: " << node->m_value.m_block_size
			    << "," << node->m_value.ref_count()
			    << std::endl;
		}
		LOG(DEBUG, "block_cache_unit::" << name << " \n"
		    << out.str());
	}
#endif	// NDEBUG_BLOCK_CACHE


} // namespace xbase

#endif	// 0
